package com.siyu.model.dispatch.algorithm;


import com.siyu.model.dispatch.algorithm.interfaces.SingleReservoirInterface;
import com.siyu.model.dispatch.io.ChukouResult;
import com.siyu.model.dispatch.io.SingleSluiceInput;
import com.siyu.model.dispatch.io.SingleSluiceOutput;
import com.siyu.model.dispatch.utils.CalculateUtils;
import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

public class RegulationSluiceDam {
    /*
     * 实例字段
     */
    private SingleSluiceInput singleSluiceInput;

    private SingleReservoirInterface damRes;

    private String folderPath;

    private double[] tempQ;

    private double[] Temp_ChuKou_Q;

    private double maxLevel;  // 最高水位，在调度建议中计算出来的

    private SingleSluiceOutput singleSluiceOutput;  // 闸坝输出类对象

    /*
     * 构造器和 getter、setter
     */
    public RegulationSluiceDam(SingleSluiceInput singleSluiceInput, SingleReservoirInterface damRes, String folderPath) {
        /*
         * 设置 RegulationSluiceDam 对象属性值
         */
        this.singleSluiceInput = singleSluiceInput;
        this.damRes = damRes;
        this.folderPath = folderPath;
    }

    public SingleSluiceOutput getSingleSluiceOutput() {
        return singleSluiceOutput;
    }

    /**
     * 计算闸坝相关属性值的前置步骤：马斯京根河道演算
     *
     * @return
     */
    public ChukouResult muskingenCalculate() throws IOException {
        MuskingenMethod OneChannel = new MuskingenMethod();

        double[] OptimalLineQrelease0 = damRes.getOptimalLine_Qrelease()[0];  // 最优调度线上各时段下泄流量
        int length = OptimalLineQrelease0.length;

        Temp_ChuKou_Q = new double[length];

        tempQ = OneChannel.msjg(OptimalLineQrelease0);
        for (int j = 0; j < length; j++) {
            Temp_ChuKou_Q[j] = damRes.getIntervalRunoffData()[0][j]; // 其他支流直接相加，没有经过河道演算
        }

        // 输出马斯京根演算后的出口断面流量
        List<Double> TempQ = new ArrayList<>();
        for (int j = 0; j < length; j++) {
            TempQ.add(tempQ[j] + Temp_ChuKou_Q[j]);
        }

        ChukouResult chukouResult = new ChukouResult();
        chukouResult.setQ(TempQ);

        // 输出到文档，后面可以不要
        String path = folderPath + File.separator + "ChukouFlowProcessObtainedByMuskingenMethod (MinimumDischarge).txt";
        StringBuilder OneLine = new StringBuilder();
        ArrayList<String> list = new ArrayList<>();
        DecimalFormat myDF = new DecimalFormat("0");
        for (int j = 0; j < length; j++) {
            String s = myDF.format(tempQ[j] + Temp_ChuKou_Q[j]);
            OneLine.append(s).append("\t");
        }
        list.add(OneLine.toString());
        File file = new File(path);
        FileUtils.writeLines(file, "UTF-8", list);

        return chukouResult;
    }

    /**
     * 闸坝主要计算过程
     *
     * @param tempQ
     * @throws ParseException
     */
    public void calculate(double[]... tempQ) throws ParseException {
        singleSluiceOutput = new SingleSluiceOutput();  // 闸坝属性值输出类对象
        /*
         * 给闸坝输入类设置初始化属性值
         */
        int n = tempQ[0].length;
        double[] tempQSum = new double[n];
        ArrayList<Double> listSluiceInput = new ArrayList<>();
        for (int i = 0; i < n; i++) {
            double v = 0;
            for (double[] doubles : tempQ) {
                v += doubles[i];
            }
            listSluiceInput.add(v);
            tempQSum[i] = v;
        }
        singleSluiceInput.setOperationRunoffData(listSluiceInput);

        /*
         * 计算闸坝输出类属性值
         */
        String name = singleSluiceInput.getSluiceName();
        String detailedSchedulingSuggestions = calculateDetailedSchedulingSuggestions(singleSluiceInput.getOperationRunoffData(),
                name, singleSluiceInput.getStartTime());
        String simpleSchedulingSuggestions = calculateSimpleSchedulingSuggestions(singleSluiceInput.getOperationRunoffData(),
                name, singleSluiceInput.getStartTime());
        String waterLevelComparisonResult = calculateWaterLevelComparisonResult(singleSluiceInput.getOperationRunoffData(), name);
        singleSluiceOutput.setDetailedSchedulingSuggestions(detailedSchedulingSuggestions);
        singleSluiceOutput.setSimpleSchedulingSuggestions(simpleSchedulingSuggestions);
        singleSluiceOutput.setWaterLevelComparisonResult(waterLevelComparisonResult);

        // 初始水位
        singleSluiceOutput.setInitialLevel(tempQSum[0]);

        // 入库洪峰（闸坝不蓄水，入库和出库一样）
        singleSluiceOutput.setFloodPeak(Collections.max(singleSluiceInput.getOperationRunoffData()));
        // 最大泄洪（出库洪峰）
        singleSluiceOutput.setFloodDischarge(Collections.max(singleSluiceInput.getOperationRunoffData()));

        // 入库峰现时间、最高水位出现时间、出库峰现时间
        int timeSpan = CalculateUtils.getMaxValueIndex(tempQSum);
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date date = simpleDateFormat.parse(singleSluiceInput.getStartTime());
        Calendar instance = Calendar.getInstance();
        instance.setTime(date);
        instance.add(Calendar.HOUR, timeSpan);
        String dateString = simpleDateFormat.format(instance.getTime());
        singleSluiceOutput.setRKPeakTime(dateString);  // 入库峰现时间
        singleSluiceOutput.setMaxLevelTime(dateString);  // 最高水位出现时间
        singleSluiceOutput.setCKPeakTime(dateString);  // 出库峰现时间

        // 最高水位（在 calculateWaterLevelComparisonResult 中计算所得）
        singleSluiceOutput.setMaxLevel(maxLevel);

        // TODO: 暂定为 false，实际值待修正
        // 最高水位是否超标准、最高水位是否超标准、末水位是否超过汛期
        singleSluiceOutput.setMaxLevelExceedStandard(false);
        singleSluiceOutput.setMaxLevelExceedLimit(false);
        singleSluiceOutput.setEndLevelExceedLimit(false);

        // 入库洪量（闸坝不蓄水，入库和出库一样）
        singleSluiceOutput.setFloodVolume(Arrays.stream(tempQSum).sum() * damRes.getPeriodLength() / 100000000);
        // 出库洪量
        singleSluiceOutput.setCKVolume(Arrays.stream(tempQSum).sum() * damRes.getPeriodLength() / 100000000);

        // 调度期末水位
        singleSluiceOutput.setEndLevel(tempQSum[tempQSum.length - 1]);

        /*
         * 计算超标准水位时段和超讯限水位时段
         * 注：闸坝暂定简化为 "--"，因为目前获取不到闸坝的时段水位 OptimalLine_level
         */
//        String overStandard = CalculateUtils.overWaterLevel(singleSluiceOutput.getOptimalLine_Level(), designWaterLevel);
        String overStandard = "--";
        singleSluiceOutput.setOverStandardTime(overStandard);

//        String overLimit = CalculateUtils.overWaterLevel(singleSluiceOutput.getOptimalLine_Level(), floodLevel);
        String overLimit = "--";
        singleSluiceOutput.setOverLimitTime(overLimit);
    }

    /**
     * @param input
     * @param name
     * @return
     */
    public double[] RegulationCalculation(double[] input, String name) {
        double[] Output = new double[input.length];
        // 目前没数据，假定出流等于入流
        System.arraycopy(input, 0, Output, 0, input.length);
        return Output;
    }

    /**
     * TODO: 该方法用途待标注
     *
     * @param input
     * @param name  闸坝名称
     * @return
     */
    public String calculateDetailedSchedulingSuggestions(List<Double> input, String name, String startTime) throws ParseException {
        // 全部用流量进行判断，后期在涉及水位判断
        double[] inputArr = input.stream().mapToDouble(d -> d).toArray();
        double[] arr;
        String[] message;

        if (Objects.equals(name, "良口")) {
            arr = new double[]{1821};
            message = new String[]{"良口上游洪水流量Q上<1821 m³/s，控制水位在66.04m~66.34m，闸门按照e=1.0m、e=2.5m、e=3.5m 3个级别控制，" + "开闸顺序按照3#、1#、5#、 2#、4#的顺序控制;", "良口上游洪水流量Q上>=1821 m³/s时，闸门全开泄洪，并将闸门的提升高度升至上限位置，不阻碍行洪，以确保工程安全运行"};
        } else if (Objects.equals(name, "塘料")) {
            arr = new double[]{1055};
            message = new String[]{"塘料水位不超过55.32m（即流量小于1055m³/s），不开闸，直至水位达到55.32m;", "塘料水位超过55.32m（即流量大于1055m³/s），闸门按照既定规程（分级开启、对称开启）开启，控制水位在55.32m，直至闸门全部开启"};
        } else if (Objects.equals(name, "卫东")) {
            arr = new double[]{700};
            message = new String[]{"卫东水位不超过48.61m（即流量小于700m³/s），不开闸，直至水位达到48.61m;", "卫东水位超过48.61m（即流量大于700m³/s），闸门按照既定规程（分级开启、对称开启）开启，控制水位在48.61m，直至闸门全部开启"};
        } else if (Objects.equals(name, "大坳")) {
            arr = new double[]{50, 100, 400};
            message = new String[]{"大坳上游洪水流量Q上<50 m³/s，控制水位在23.91m，闸门按照e=0.1m控制，开闸顺序按照17#、15#、19#、13#、21#、……、1#、33#、18#、20#、16#、22#、14#……的顺序控制;", "大坳上游洪水流量Q上<100 m³/s，控制水位在23.91m，闸门按照e=0.2m控制，开闸顺序按照17#、15#、19#、13#、21#、……、1#、33#、18#、20#、16#、22#、14#……的顺序控制;", "大坳上游洪水流量Q上<400 m³/s，控制水位在23.91m，闸门按照e=0.6m、e=0.8m、e=1.0m 3个级别控制，开闸顺序按照17#、15#、19#、13#、21#、……、1#、33#、18#、20#、16#、22#、14#……的顺序控制;", "大坳上游洪水流量Q上>400 m³/s，把闸门全开泄洪，以确保工程的安全运行"};
        } else if (Objects.equals(name, "牛心岭")) {
            arr = new double[]{1432};
            message = new String[]{"牛心岭水位不超过17.50m（即流量小于1432m³/s），不开闸，直至水位达到17.50m;", "牛心岭水位超过17.50m（即流量大于1432m³/s），闸门按照既定规程（分级、对称开启）开启，控制水位在17.50m，直至闸门全部开启"};
        } else if (Objects.equals(name, "李溪")) {
            arr = new double[]{1300};
            message = new String[]{"李溪上游洪水流量Q上<1300m³/s，维持拦河坝上游蓄水位10.51m至10.65m。闸门按照e=0.1m，e=0.2m、e=0.6m、e=0.8m、e=1.0m 5个级别控制，" + "开闸顺序按照8#、10#、6#、12#、4#、14#、2#、9#、7#、11#、5#、13#的顺序开启;", "李溪上游洪水流量Q上>1300m³/s,若继续增大时或水位达到11.27m时，将大坝闸门提升至最大开度行程4m"};
        } else if (Objects.equals(name, "人和")) {
            arr = new double[]{500, 800};
            message = new String[]{"人和上游洪水流量Q上<500m³/s，为维持水位在5.3m，闸门按照e=0.1m，e=0.4m、e=0.6m、e=0.8m、e=1.0m 5个级别控制，开闸顺序按照6、8、4、10、2、12、5、7、3、9、1、11顺序开启;", "人和上游洪水流量500m³/s≤ Q上≤800m³/s时，按闸前水位5.3m控制闸门开度至全开;", "人和上游洪水流量Q上>800m³/s，泄洪闸全部开启泄。当水位高于设计洪水位（5.6m）低于校核洪水位（6.6m）时：输水涵及放水设施全部打开放水，溢洪道自由泄流。" + "当水位高于校核洪水位（6.6m）时：输水涵及放水设施全部打开，及时做好下游可能受影响村庄群众的全部撤离工作；泄洪过程中，如下游发生意外情况，并根据指令采取相应的应急措施"};
        } else if (Objects.equals(name, "初溪")) {
            arr = new double[]{147.9, 250};
            message = new String[]{"初溪上游洪水流量Q上<147.9m³/s，闸门全部关闭，上游挡水蓄至正常高蓄水位6.00m;", "初溪上游洪水流量147.9m³/s≤ Q上≤250m³/s时，将部分开启（开启孔数不少于5孔），泄放剩余流量，闸前水位仍保持6.00m不变;", "初溪上游洪水流量Q上>250m³/s，全部开启泄洪，闸门全开时，要从小开度并以1组至4组顺序开启，然后逐步增加开度循环增大，洪峰过后再行关闭闸门蓄水至６.00m。" + "拦河闸坝闸门开启方式：闸门分组开启，每组同时开启，第一组包括1#、5#、9#、13#、17#、21#闸门：第二组包括3#、7#、11#、15#、19# 闸门；" + "第三组包括2#、6#、10#、14#、18#闸门；第四组包括4#、8#、12#、16#、20#闸门。闸门的开启组数多少根据下泄流量多少而定"};
        } else if (Objects.equals(name, "正果")) {
            // TODO: 暂时没有数据，不处理
            return null;
        } else {
            throw new RuntimeException("name 无法匹配！！");
        }

        return getSchedulingSuggestions(inputArr, arr, message, startTime);
    }

    /**
     * TODO: 该方法用途待标注
     *
     * @param input
     * @param name
     * @return
     */
    public String calculateSimpleSchedulingSuggestions(List<Double> input, String name, String startTime) throws ParseException {
        double[] inputArr = input.stream().mapToDouble(d -> d).toArray();
        double[] arr;
        String[] message;

        if (Objects.equals(name, "良口")) {
            arr = new double[]{1821};
            message = new String[]{"良口控制水位在66.04m~66.34m;", "良口闸门全开泄洪"};
        } else if (Objects.equals(name, "塘料")) {
            arr = new double[]{1055};
            message = new String[]{"塘料不开闸，直至水位达到55.32m;", "塘料闸门按照既定规程开启，控制水位在55.32m"};
        } else if (Objects.equals(name, "卫东")) {
            arr = new double[]{700};
            message = new String[]{"卫东不开闸，直至水位达到48.61m;", "卫东闸门按照既定规程开启，控制水位在48.61m"};
        } else if (Objects.equals(name, "大坳")) {
            arr = new double[]{50, 100, 400};
            message = new String[]{"大坳控制水位在23.91m，闸门按照e=0.1m控制;", "大坳控制水位在23.91m，闸门按照e=0.2m控制;", "大坳控制水位在23.91m，闸门按照0.6m、0.8m、1.0m 3个级别控制;", "大坳闸门全开泄洪"};
        } else if (Objects.equals(name, "牛心岭")) {
            arr = new double[]{1432};
            message = new String[]{"牛心岭不开闸，直至水位达到17.50m;", "牛心岭闸门按照既定规程开启，控制水位在17.50m"};
        } else if (Objects.equals(name, "李溪")) {
            arr = new double[]{1300};
            message = new String[]{"李溪维持水位10.51m至10.65m。闸门按照既定的5个级别控制;", "李溪若入流继续增大或水位达到11.27m，将大坝闸门提升至最大开度行程4m"};
        } else if (Objects.equals(name, "人和")) {
            arr = new double[]{500, 800};
            message = new String[]{"人和维持水位在5.3m，闸门按照既定的5个级别控制;", "人和按闸前水位5.3m控制，闸门开度至全开;", "人和泄洪闸全部开启泄"};
        } else if (Objects.equals(name, "初溪")) {
            arr = new double[]{147.9, 250};
            message = new String[]{"初溪维持水位在6m，闸门全部关闭;", "初溪按闸前水位6m控制，闸门部分开启;", "初溪泄洪闸全部开启泄"};
        } else if (Objects.equals(name, "正果")) {
            // TODO: 暂时没有数据，不处理
            return null;
        } else {
            throw new RuntimeException("name 匹配不到！！");
        }
        return getSchedulingSuggestions(inputArr, arr, message, startTime);
    }

    /**
     * TODO: 该方法用途待标注
     *
     * @param input
     * @param name
     * @return
     */
    public String calculateWaterLevelComparisonResult(ArrayList<Double> input, String name) {
        double[] inputArr = input.stream().mapToDouble(d -> d).toArray();
        double maxQ = CalculateUtils.getMaxValue(inputArr);
        double maxLevelIndex = CalculateUtils.getMaxValueIndex(inputArr);
        double threshold;
        String nameCN;
        double[][] curveLQ;

        if (Objects.equals(name, "良口")) {
            curveLQ = new double[][]{{63.3, 585}, {63.43, 657}, {66, 1541}, {66.69, 1821}, {67.04, 2004}};
            threshold = 67.38;
            nameCN = "良口";
        } else if (Objects.equals(name, "塘料")) {
            curveLQ = new double[][]{{54.68, 863}, {55.24, 1025}, {56.66, 1557}, {57.21, 1818}, {57.72, 2077}};
            threshold = 67.38;
            nameCN = "塘料";
        } else if (Objects.equals(name, "卫东")) {
            curveLQ = new double[][]{{49.01, 875}, {49.57, 1120}, {50.56, 1608}, {51.04, 1870}, {51.53, 2129}};
            threshold = 51.04;
            nameCN = "卫东";
        } else if (Objects.equals(name, "大坳")) {
            curveLQ = new double[][]{{25.4, 1400}, {25.8, 1700}, {26.2, 1980}};
            threshold = 26.23;
            nameCN = "大坳";
        } else if (Objects.equals(name, "牛心岭")) {
            curveLQ = new double[][]{{15.9, 849}, {16, 994}, {16.74, 1220}, {18.57, 1730}, {19.53, 1921}, {20.41, 2080}, {21.04, 2350}};
            threshold = 20.41;
            nameCN = "牛心岭";
        } else if (Objects.equals(name, "李溪")) {
            curveLQ = new double[][]{{9.7, 600}, {10.2, 1000}, {10.5, 1400}, {11.05, 2000}, {11.4, 2400}};
            threshold = 11.27;
            nameCN = "李溪";
        } else if (Objects.equals(name, "人和")) {
            curveLQ = new double[][]{{4.25, 1200}, {4.9, 1500}, {6, 2100}, {6.2, 2250}, {6.7, 2550}};
            threshold = 6.6;
            nameCN = "人和";
        } else if (Objects.equals(name, "初溪")) {
            curveLQ = new double[][]{{7.11, 2266}, {7.9, 2841.1}, {8.56, 3416.3}, {9.22, 4165.9}, {10.16, 4723.6}};
            threshold = 8.56;
            nameCN = "初溪";
        } else if (Objects.equals(name, "正果")) {
            // TODO: 暂时没有数据，不处理
            return null;
        } else {
            throw new RuntimeException("name 匹配不到！！");
        }
        maxLevel = CalculateUtils.getDownLevelFromLQ(maxQ, curveLQ);
        return String.format("第%s时段，%s入流最大，为%.1fm³/s,对应的最高水位为%.2fm,%s超过防洪标准对应的%sm", maxLevelIndex, nameCN, maxQ, maxLevel, maxLevel > threshold ? "" : "未", threshold);
    }

    /**
     * 获取 SchedulingSuggestions
     *
     * @param inputArr
     * @param arr
     * @param strs
     * @return
     */
    private String getSchedulingSuggestions(double[] inputArr, double[] arr, String[] strs, String startTime) throws ParseException {
        StringBuilder ans = new StringBuilder();
        int n = strs.length;
        int m = arr.length;
        StringBuilder[] builder = new StringBuilder[strs.length];
        for (int i = 0; i < n; i++) {
            builder[i] = new StringBuilder();
        }
        for (int i = 0; i < inputArr.length; i++) {
            // 每个时段逐一判断
            int j = 0;
            for (; j < m; j++) {
                if (inputArr[i] < arr[j]) break;
            }
            builder[j].append(i).append(",");
        }
        for (int i = 0; i < n; i++) {
            if (builder[i].length() != 0) {
                ans.append("第").append(CalculateUtils.printNums(builder[i].toString(), startTime)).append("时段，").append(strs[i]);
            }
        }
        return ans + "\n";
    }
}
